package com.qq5194102.chaindestroy;

import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.ExperienceOrb;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;

import java.util.*;

/**
 * @Author caiyh
 * @Date 2025/7/27 1:35
 * @Description 连锁挖矿监听器（经验计算修复版）
 */
public class VeinMinerListener implements Listener {
    private final ChainDestroy.PluginConfig config;
    private final JavaPlugin plugin;
    private final Random random = new Random();

    // 支持的矿石集合 (1.12.2版本)
    private final Set<Material> SUPPORTED_ORES = EnumSet.of(
            Material.DIAMOND_ORE,
            Material.COAL_ORE,
            Material.IRON_ORE,
            Material.GOLD_ORE,
            Material.LAPIS_ORE,
            Material.REDSTONE_ORE,
            Material.GLOWING_REDSTONE_ORE, // 激活的红石矿
            Material.EMERALD_ORE,
            Material.QUARTZ_ORE
    );

    // 矿石经验值规则
    private final Map<Material, Integer[]> ORE_EXPERIENCE = new HashMap<Material, Integer[]>() {{
        put(Material.DIAMOND_ORE, new Integer[]{3, 7});
        put(Material.EMERALD_ORE, new Integer[]{3, 7});
        put(Material.LAPIS_ORE, new Integer[]{2, 5});
        put(Material.REDSTONE_ORE, new Integer[]{1, 5});
        put(Material.GLOWING_REDSTONE_ORE, new Integer[]{1, 5});
        put(Material.QUARTZ_ORE, new Integer[]{1, 5});
        put(Material.COAL_ORE, new Integer[]{2, 2});
        // 铁矿石和金矿石没有经验
    }};

    public VeinMinerListener(JavaPlugin plugin, ChainDestroy.PluginConfig config) {
        this.plugin = plugin;
        this.config = config;
    }

    /**
     * 处理方块破坏事件
     * @param event 方块破坏事件
     */
    @EventHandler
    public void onBlockBreak(BlockBreakEvent event) {
        // 验证前置条件是否满足
        if (!validatePreconditions(event)) return;

        Player player = event.getPlayer();
        Block startBlock = event.getBlock();
        ItemStack tool = player.getInventory().getItemInMainHand();

        // 执行连锁挖矿
        int blocksMined = mineConnectedOres(startBlock, player, tool, config.MAX_VEIN_BLOCKS);

        // 玩家反馈
        if (blocksMined > 1) {
            //player.sendMessage(ChatColor.GREEN + "连锁挖矿触发! 破坏了 " + blocksMined + " 个矿石");
        }
    }

    /**
     * 验证连锁挖矿的触发条件
     * @param event 方块破坏事件
     * @return 是否满足触发条件
     */
    private boolean validatePreconditions(BlockBreakEvent event) {
        Player player = event.getPlayer();
        Block block = event.getBlock();
        ItemStack tool = player.getInventory().getItemInMainHand();

        // 检查游戏模式和基础条件
        if (player.getGameMode() == GameMode.CREATIVE) return false;
        if (config.REQUIRE_SNEAKING && !player.isSneaking()) return false;
        if (!tool.getType().name().endsWith("_PICKAXE")) return false;
        if (!SUPPORTED_ORES.contains(block.getType())) return false;
        if (!player.hasPermission("chaindestroy.veinminer")) return false;

        return true;
    }

    /**
     * 挖取相连的同种矿石
     * @param startBlock 起始方块
     * @param player 玩家
     * @param tool 工具
     * @param maxBlocks 最大连锁数
     * @return 实际挖掘的矿石数量
     */
    private int mineConnectedOres(Block startBlock, Player player, ItemStack tool, int maxBlocks) {
        Material targetType = startBlock.getType();
        Queue<Block> queue = new LinkedList<>();
        Set<Block> minedBlocks = new HashSet<>();
        List<BlockInfo> brokenBlocks = new ArrayList<>(); // 记录所有破坏的方块信息

        // 初始化队列
        queue.add(startBlock);
        minedBlocks.add(startBlock);

        int count = 0;
        while (!queue.isEmpty() && count < maxBlocks) {
            // 每次循环前检查工具状态
            if (isToolBroken(tool)) {
                player.sendMessage(ChatColor.RED + "工具已损坏，停止连锁!");
                break;
            }

            Block current = queue.poll();

            // 只处理目标类型的矿石
            if (isValidOreBlock(current, targetType)) {
                // 在破坏前保存方块信息
                BlockInfo blockInfo = new BlockInfo(current.getLocation(), current.getType());

                // 安全破坏方块并处理经验掉落
                if (breakBlockManually(current, player, tool)) {
                    count++;
                    brokenBlocks.add(blockInfo); // 记录破坏的方块信息

                    // 添加相邻方块到队列（只添加目标矿石）
                    addAdjacentOres(current, queue, minedBlocks, targetType);
                }
            }
        }

        // 最后统一给予经验（解决经验球消失问题）
        if (!brokenBlocks.isEmpty()) {
            giveExperienceForBlocks(brokenBlocks, player, tool);
        }

        return count;
    }

    /**
     * 手动破坏方块并处理掉落物
     * @param block 要破坏的方块
     * @param player 破坏方块的玩家
     * @param tool 使用的工具
     * @return 是否成功破坏
     */
    private boolean breakBlockManually(Block block, Player player, ItemStack tool) {
        // 跳过无效方块
        if (block.isEmpty() || block.getType() == Material.BEDROCK) return false;

        // 保存原始类型
        Material originalType = block.getType();
        Material effectiveType = originalType;

        // 处理红石矿激活状态
        if (originalType == Material.GLOWING_REDSTONE_ORE) {
            effectiveType = Material.REDSTONE_ORE;
        }

        try {
            // 1. 获取精准采集状态
            boolean silkTouch = tool.containsEnchantment(Enchantment.SILK_TOUCH);

            // 2. 获取掉落物（考虑时运附魔）
            Collection<ItemStack> drops = getCustomDrops(effectiveType, tool, silkTouch);

            // 3. 生成掉落物
            for (ItemStack item : drops) {
                block.getWorld().dropItemNaturally(block.getLocation(), item);
            }

            // 4. 设置方块为空气
            block.setType(Material.AIR);

            // 5. 扣除工具耐久
            if (hasDurability(tool)) {
                short newDurability = (short) (tool.getDurability() + 1);
                tool.setDurability(newDurability);
            }

            return true;
        } finally {
            // 恢复红石矿的原始状态（如果之前修改过）
            if (originalType == Material.GLOWING_REDSTONE_ORE) {
                block.setType(originalType);
            }
        }
    }

    /**
     * 自定义掉落物计算（处理时运和精准采集）
     * @param blockType 方块类型
     * @param tool 工具
     * @param silkTouch 是否有精准采集
     * @return 掉落物集合
     */
    private Collection<ItemStack> getCustomDrops(Material blockType, ItemStack tool, boolean silkTouch) {
        List<ItemStack> drops = new ArrayList<>();

        // 精准采集处理
        if (silkTouch) {
            // 直接掉落方块本身
            drops.add(new ItemStack(blockType));
            return drops;
        }

        // 根据矿石类型处理
        switch (blockType) {
            case DIAMOND_ORE:
                drops.add(new ItemStack(Material.DIAMOND, calculateFortuneDrops(tool, 1, 4)));
                break;
            case EMERALD_ORE:
                drops.add(new ItemStack(Material.EMERALD, calculateFortuneDrops(tool, 1, 4)));
                break;
            case COAL_ORE:
                drops.add(new ItemStack(Material.COAL, calculateFortuneDrops(tool, 1, 4)));
                break;
            case LAPIS_ORE:
                int baseLapis = 4 + random.nextInt(6); // 4-9
                int lapisCount = calculateFortuneDrops(tool, baseLapis, 36);
                drops.add(new ItemStack(Material.INK_SACK, lapisCount, (short) 4)); // 青金石染料
                break;
            case REDSTONE_ORE:
                int baseRedstone = 4 + random.nextInt(2); // 4-5
                int redstoneCount = calculateFortuneDrops(tool, baseRedstone, 20);
                drops.add(new ItemStack(Material.REDSTONE, redstoneCount));
                break;
            case QUARTZ_ORE:
                drops.add(new ItemStack(Material.QUARTZ, calculateFortuneDrops(tool, 1, 4)));
                break;
            case IRON_ORE:
                drops.add(new ItemStack(Material.IRON_ORE)); // 铁矿石不掉落铁锭
                break;
            case GOLD_ORE:
                drops.add(new ItemStack(Material.GOLD_ORE)); // 金矿石不掉落金锭
                break;
            default:
                // 其他矿石默认掉落自身
                drops.add(new ItemStack(blockType));
        }

        return drops;
    }

    /**
     * 计算时运附魔影响下的掉落数量（修正版）
     * @param tool 工具
     * @param baseAmount 基础数量
     * @param maxAmount 最大数量
     * @return 实际掉落数量
     */
    private int calculateFortuneDrops(ItemStack tool, int baseAmount, int maxAmount) {
        if (!tool.containsEnchantment(Enchantment.LOOT_BONUS_BLOCKS)) {
            return baseAmount;
        }

        int fortuneLevel = tool.getEnchantmentLevel(Enchantment.LOOT_BONUS_BLOCKS);

        // 修正后的时运算法（符合原版规则）
        if (fortuneLevel > 0) {
            // 计算额外掉落数量
            int bonus = random.nextInt(fortuneLevel + 1);
            int total = baseAmount * (bonus + 1);

            // 确保不超过最大数量
            return Math.min(total, maxAmount);
        }

        return baseAmount;
    }

    /**
     * 为所有破坏的方块统一给予经验
     * @param blocks 破坏的方块信息列表
     * @param player 玩家
     * @param tool 工具
     */
    private void giveExperienceForBlocks(List<BlockInfo> blocks, Player player, ItemStack tool) {
        // 计算总经验值
        int totalExp = 0;
        boolean silkTouch = tool.containsEnchantment(Enchantment.SILK_TOUCH);

        for (BlockInfo blockInfo : blocks) {
            Material type = blockInfo.getType();

            // 精准采集不给经验
            if (silkTouch) continue;

            // 检查是否有经验规则
            if (ORE_EXPERIENCE.containsKey(type)) {
                Integer[] range = ORE_EXPERIENCE.get(type);
                totalExp += range[0] + random.nextInt(range[1] - range[0] + 1);
            }
        }

        // 在第一个方块位置生成经验球
        if (totalExp > 0 && !blocks.isEmpty()) {
            Location location = blocks.get(0).getLocation();
            location.getWorld().spawn(location, ExperienceOrb.class).setExperience(totalExp);
            //player.sendMessage(ChatColor.GOLD + "获得经验: " + totalExp);
        }
    }

    /**
     * 添加相邻的目标矿石到搜索队列
     * @param block 中心方块
     * @param queue 方块队列
     * @param visited 已访问方块集合
     * @param targetType 目标矿石类型
     */
    private void addAdjacentOres(Block block, Queue<Block> queue, Set<Block> visited, Material targetType) {
        for (int x = -1; x <= 1; x++) {
            for (int y = -1; y <= 1; y++) {
                for (int z = -1; z <= 1; z++) {
                    // 跳过中心自身
                    if (x == 0 && y == 0 && z == 0) continue;

                    Block relative = block.getRelative(x, y, z);

                    // 只添加目标矿石类型
                    if (isValidOreBlock(relative, targetType)) {
                        if (!visited.contains(relative)) {
                            visited.add(relative);
                            queue.add(relative);
                        }
                    }
                }
            }
        }
    }

    /**
     * 检查方块是否为有效的矿石类型
     * @param block 方块
     * @param targetType 目标矿石类型
     * @return 是否有效
     */
    private boolean isValidOreBlock(Block block, Material targetType) {
        Material type = block.getType();

        // 跳过无效方块
        if (type == Material.AIR || type == Material.BEDROCK) {
            return false;
        }

        // 处理红石矿的特殊情况
        if (targetType == Material.REDSTONE_ORE || targetType == Material.GLOWING_REDSTONE_ORE) {
            return type == Material.REDSTONE_ORE ||
                    type == Material.GLOWING_REDSTONE_ORE;
        }

        return type == targetType;
    }

    /**
     * 检查工具是否具有耐久属性
     * @param tool 工具
     * @return 是否有耐久
     */
    private boolean hasDurability(ItemStack tool) {
        return tool.getType().getMaxDurability() > 0;
    }

    /**
     * 检查工具是否损坏
     * @param tool 工具
     * @return 是否损坏
     */
    private boolean isToolBroken(ItemStack tool) {
        return hasDurability(tool) && tool.getDurability() >= tool.getType().getMaxDurability();
    }

    /**
     * 方块信息类（用于保存破坏前的方块信息）
     */
    private static class BlockInfo {
        private final Location location;
        private final Material type;

        public BlockInfo(Location location, Material type) {
            this.location = location;
            this.type = type;
        }

        public Location getLocation() {
            return location;
        }

        public Material getType() {
            return type;
        }
    }
}